Skip to content

Conversation

@jpshackelford
Copy link
Contributor

@jpshackelford jpshackelford commented Jan 9, 2026

Summary

Plugin commands from the commands/ directory are now automatically converted to keyword-triggered skills and merged into the agent context.

This enables Claude Code format plugins that use commands/ (which is the standard location for slash commands) to work correctly with OpenHands - the agent now receives the command instructions as skills.

Changes

  • Add CommandDefinition.to_skill() method that converts a command to a Skill with a KeywordTrigger using Claude Code namespace format: /<plugin-name>:<command-name>
  • Add Plugin.get_all_skills() method that returns all skills from both skills/ and commands/ directories
  • Update _merge_plugin_into_request() to use get_all_skills() so commands are included when merging plugin content
  • Add comprehensive tests for the new functionality

Example

For a plugin named city-weather with a command now.md:

city-weather/
├── .claude-plugin/
│   └── plugin.json
└── commands/
    └── now.md          # Weather command instructions
  • Trigger keyword: /city-weather:now
  • When user types /city-weather:now Tokyo, the skill activates with $ARGUMENTS = Tokyo

Testing

  • Added unit tests for CommandDefinition.to_skill()
  • Added unit tests for Plugin.get_all_skills()
  • Added integration tests for _merge_plugin_into_request() with commands

All tests pass:

tests/sdk/plugin/test_plugin_loading.py - 15 passed
tests/agent_server/test_conversation_service.py::TestPluginLoading - 10 passed

Closes #1674

@jpshackelford can click here to continue refining the PR


Agent Server images for this PR

GHCR package: https://github.com/OpenHands/agent-sdk/pkgs/container/agent-server

Variants & Base Images

Variant Architectures Base Image Docs / Tags
java amd64, arm64 eclipse-temurin:17-jdk Link
python amd64, arm64 nikolaik/python-nodejs:python3.12-nodejs22 Link
golang amd64, arm64 golang:1.21-bookworm Link

Pull (multi-arch manifest)

# Each variant is a multi-arch manifest supporting both amd64 and arm64
docker pull ghcr.io/openhands/agent-server:d6d6e65-python

Run

docker run -it --rm \
  -p 8000:8000 \
  --name agent-server-d6d6e65-python \
  ghcr.io/openhands/agent-server:d6d6e65-python

All tags pushed for this build

ghcr.io/openhands/agent-server:d6d6e65-golang-amd64
ghcr.io/openhands/agent-server:d6d6e65-golang_tag_1.21-bookworm-amd64
ghcr.io/openhands/agent-server:d6d6e65-golang-arm64
ghcr.io/openhands/agent-server:d6d6e65-golang_tag_1.21-bookworm-arm64
ghcr.io/openhands/agent-server:d6d6e65-java-amd64
ghcr.io/openhands/agent-server:d6d6e65-eclipse-temurin_tag_17-jdk-amd64
ghcr.io/openhands/agent-server:d6d6e65-java-arm64
ghcr.io/openhands/agent-server:d6d6e65-eclipse-temurin_tag_17-jdk-arm64
ghcr.io/openhands/agent-server:d6d6e65-python-amd64
ghcr.io/openhands/agent-server:d6d6e65-nikolaik_s_python-nodejs_tag_python3.12-nodejs22-amd64
ghcr.io/openhands/agent-server:d6d6e65-python-arm64
ghcr.io/openhands/agent-server:d6d6e65-nikolaik_s_python-nodejs_tag_python3.12-nodejs22-arm64
ghcr.io/openhands/agent-server:d6d6e65-golang
ghcr.io/openhands/agent-server:d6d6e65-java
ghcr.io/openhands/agent-server:d6d6e65-python

About Multi-Architecture Support

  • Each variant tag (e.g., d6d6e65-python) is a multi-arch manifest supporting both amd64 and arm64
  • Docker automatically pulls the correct architecture for your platform
  • Individual architecture tags (e.g., d6d6e65-python-amd64) are also available if needed

@github-actions
Copy link
Contributor

github-actions bot commented Jan 9, 2026

Coverage

Coverage Report •
FileStmtsMissCoverMissing
openhands-sdk/openhands/sdk/plugin
   plugin.py1772387%355–357, 359–364, 381–382, 384–386, 395–399, 415–416, 434–435
   types.py210498%146, 235, 651, 659
TOTAL15793458970% 

@openhands-ai
Copy link

openhands-ai bot commented Jan 9, 2026

Looks like there are a few issues preventing this PR from being merged!

  • GitHub Actions are failing:
    • Pre-commit checks

If you'd like me to help, just leave a comment, like

@OpenHands please fix the failing actions on PR #1676 at branch `feat/plugin-commands-as-skills`

Feel free to include any additional details that might help me get this PR into a better state.

You can manage your notification settings

@jpshackelford jpshackelford changed the base branch from feat/agent-server-plugin-loading to main January 9, 2026 19:46
@jpshackelford jpshackelford force-pushed the feat/plugin-commands-as-skills branch from 32a27fc to 326b39c Compare January 16, 2026 07:01
@jpshackelford jpshackelford changed the base branch from main to feat/agent-server-plugin-loading January 16, 2026 07:02
Plugin commands from the commands/ directory are now converted to
keyword-triggered skills and merged into the agent context.

Changes:
- Add CommandDefinition.to_skill() method that converts a command to
  a Skill with a KeywordTrigger using Claude Code namespace format:
  /<plugin-name>:<command-name>
- Add Plugin.get_all_skills() method that returns all skills from both
  skills/ and commands/ directories
- Update _merge_plugin_into_request() to use get_all_skills() so
  commands are included when merging plugin content

Example:
For a plugin 'city-weather' with command 'now.md':
- Trigger keyword: '/city-weather:now'
- When user types '/city-weather:now Tokyo', the skill activates

Closes #1674

Co-authored-by: openhands <[email protected]>
- Fix line too long in log message
- Add None check for allowed_tools in test

Co-authored-by: openhands <[email protected]>
@jpshackelford jpshackelford force-pushed the feat/plugin-commands-as-skills branch from 326b39c to f22766c Compare January 16, 2026 18:19
Copy link
Collaborator

@enyst enyst left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you please tell, does this PR imply that currently, "/command" doesn't trigger?

If we make them consistent, the old triggered skills with the new commands, could we maybe make the command be the canonical one, if that makes sense?

@jpshackelford
Copy link
Contributor Author

@enyst Exactly. The Claude plugin format has a command subdirectory and the PR we were looking at before didn't do the work of wiring up skills in that subdirectory to trigger with a slash command.

Claude / AgentSkills semantics for triggering skills is different from the way OpenHands traditionally does it. When we implemented the basic AgentSkills support we moved toward those semantics but the initial plugin support didn't handle wiring up skills in the commands folder to be registered as triggered skills.

(The AgentSkills standard doesn't cover this--it's part of the Claude plugin semantics which are outside the scope of AgentSkills. We are working toward not just AgentSkills compatibility but Claude plugin compatibility.)

@jpshackelford
Copy link
Contributor Author

@enyst Since I've got some significant work ahead of me to get #1651 refactored into the desired shape I think it might make sense to just merge this into that branch and then when the refactoring is done and merged we'll have the plugin loading MVP done for software-agent-sdk, and I can then focus on the app server change (OpenHands/OpenHands#12321).

@enyst
Copy link
Collaborator

enyst commented Jan 16, 2026

Thank you. I think I hear you saying that commands should be skills-with-trigger. I'm trying to say that maybe skills-with-trigger should be interpreted as commands, instead.

Precisely because Agentskill standard doesn't cover "triggered skills" in old OpenHands sense. Instead, plugin semantics make "triggered stuff" commands.

It's not a big distinction, I'm sure. Just the objects (CommandDefinition I think it was?) that we'd use in the rest of the code for both. Does that make sense?

@jpshackelford
Copy link
Contributor Author

I love the idea of a more first-class representation of commands! I'd likely would need some pointers to be able to execute on that, but conceptually all for it.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Plugin commands should be loaded as keyword-triggered skills

4 participants